/*
 * jquery.juice.suggest.js
 *
 * Juice Library Suggest v0.1.0
 * Date: 2009-09-05
 * Requires: jQuery v1.3 or later
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Copyright 2009 Steve Whiteley (http://jui.ce.it)
 */

(function($) {
	$.fn.suggest = function(callerSettings) {
		var settings = $.extend(true, {}, $.fn.suggest.settings, callerSettings);
		return this.each(function() {
			var n = $(this), s = {};
			var setup = function() {
				s.selected = null;
				s.options = $('<div />')
					.addClass(settings.className)
					.attr('id', 'juice-suggest')
					.css({
						position: 'absolute',
						top: 0,
						left: 0,
						zIndex: 1000
					})
					.hide()
					.appendTo('body');
				s.iframe = $('<iframe frameborder="0" />')
					.css({
						width: '100%',
						position: 'absolute',
						top: 0,
						left: 0
					});
				if ($.browser.msie && $.browser.version == 6) {
					s.iframe.appendTo(s.options);
				}
				n.keyup(function(e) {
					if (e.keyCode > 40 || e.keyCode == 8) {
						if ($.isFunction(settings.onChange)) {
							settings.onChange(n, n.val());
						}
						if (n.val() != '' && n.val().length >= settings.minLength) {
							clearTimeout(s.delay);
							s.delay = setTimeout(function() {
								var xhr = $.ajax({
									url: settings.url,
									data: $.extend(true, { 'value' : n.val() }, settings.data),
									dataType: 'json',
									type: settings.method || 'GET',
									success: function(data) {
										if ($.isFunction(settings.onResponse)) {
											settings.onResponse(n, data, xhr.responseText);
										}
										s.options.empty();
										if (data.length) {
											$.each(data, addItem);
											setSelected(0);
										} else {
											setSelected(null);
										}
									}
								});
							}, settings.delay);
						} else {
							hide();
						}
					} else if (e.keyCode == 38) {
						e.preventDefault();
						setSelected(s.selected - 1);
					} else if (e.keyCode == 40) {
						e.preventDefault();
						setSelected(s.selected + 1);
					} else if (e.keyCode == 27) {
						setSelected(null);
					}
				})
				.keypress(function(e) {
					if (e.keyCode == 13 && s.selected !== null) {
						e.preventDefault();
						setValue();
					}
				})
				.blur(function(e) {
					setTimeout(function() {
						setSelected(null);
					}, 200);
				})
				.attr('autocomplete', 'off');
			};
			var addItem = function(i, item) {
				var value = item, list = s.list;
				if (typeof item == 'object') {
					value = item.data.value;
					if (item.group) {
						if ($('ul', s.options).length == 0) {
							s.list = $('<ul />').appendTo(s.options);
						}
						s.options.addClass('juice-suggest-grouped');
						list = $('li ol.' + item.group);
						if (list.length < 1) {
							s.list.addClass('groups');
							var group = $('<li />')
								.appendTo(s.list)
								.append('<label>' + item.group + '</label>');
							list = $('<ol />')
								.addClass(item.group)
								.appendTo(group);
						}
					}
				} else if ($('ol', s.options).length == 0) {
					s.options.addClass('juice-suggest-ungrouped');
					s.list = list = $('<ol />').appendTo(s.options);
				}
				option = $('<li />')
					.text(value)
					.appendTo(list)
					.click(function() {
						n.val(value);
						hide();
					})
					.mouseover(function() {
						if (item.group) {
							var current = this;
							$('ol > li', s.list).each(function(i, item) {
								if (item == current) {
									setSelected(i);
								}
							});
						} else {
							setSelected(i);
						}
					})
					.data('details', item.data);
			};
			var setSelected = function(item) {
				var items = $('ol > li', s.options);
				s.selected = item;
				if (s.selected === null) {
					hide();
					return;
				}
				if (s.selected < 0) {
					s.selected = items.length - 1;
				} else if (s.selected >= items.length) {
					s.selected = null;
				}
				items
					.removeClass('selected')
					.eq(s.selected)
					.addClass('selected');
				if ($.isFunction(settings.onMouseover)) {
					settings.onMouseover(n, items.eq(s.selected));
				}
				show();
			};
			var setValue = function() {
				var item = $('ol > li', s.options).eq(s.selected);
				var details;
				if (details = item.data('details')) {
					n.val(details.value);
				} else {
					n.val(item.text());
				}
				if ($.isFunction(settings.onSelect)) {
					settings.onSelect(n, items.eq(s.selected));
				}
				setSelected(null);
			};
			var position = function() {
				var pos = n.offset();
				s.options
					.css({
						top: pos.top + n.outerHeight() + settings.offset,
						left: pos.left,
						width: settings.width ? settings.width : Math.max(settings.minWidth, n.innerWidth())
					});
			};
			var show = function() {
				if ($('li', s.list).length > 0) {
					position();
					$(window).bind('resize.suggest', position);
					s.options
						.css('width', settings.width ? settings.width : Math.max(settings.minWidth, n.outerWidth()))
						.fadeIn(settings.fade, function() {
							s.iframe.css('height', s.options.innerHeight());
						});
				}
			};
			var hide = function() {
				$(window).unbind('.suggest');
				s.options.fadeOut(settings.fade);
			};
			if (this.tagName.toUpperCase() == 'INPUT') {
				setup();
			}
		});
	};
	$.fn.suggest.settings = {
		className: 'juice-suggest',
		delay: 200,
		fade: 0,
		minLength: 1,
		minWidth: false,
		method: 'GET',
		offset: 0,
		onChange: false,
		onMouseover: false,
		onResponse: false,
		onSelect: false,
		url: false,
		width: false
	};
})(jQuery);